package server.rest;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.math.NumberUtils;
import server.rest.filter.RSFFilter;

public class Handler
{
    Object object;
    Method method;
    RestfulHandlerConfig handlerConfig;
    RSFFilter filter;
    java.util.ArrayDeque<RSFFilter> filters = new java.util.ArrayDeque<RSFFilter>();

    public Object getObject()
    {
        return object;
    }

    public Method getMethod()
    {
        return method;
    }

    Class<?> requestType;
    String basePath;

    public String getBasePathSchema()
    {
        return this.basePath + this.handlerConfig.getSchema();
    }

    public enum InvokeParams
    {

        ERROR(0), CONTEXT(2), JSON_CONTEXT(3), JSON_CONTEXT_JSON(11), CONTEXT_JSON(10),
        CONTEXT_SESSION(6), JSON_CONTEXT_SESSION(7), JSON_CONTEXT_SESSION_JSON(15), CONTEXT_SESSION_JSON(14);
        int state;

        private InvokeParams(int value)
        {
            this.state = value;
        }

        public static final InvokeParams fromNumber(final Integer value)
        {
            for (InvokeParams inv : InvokeParams.values())
            {
                if (inv.state == value)
                {
                    return inv;
                }
            }
            return ERROR;
        }

        public static final InvokeParams fromString(final String value)
        {
            if (NumberUtils.isNumber(value))
            {
                InvokeParams inv = fromNumber(Integer.valueOf(value, 2));
                if (ERROR.equals(inv))
                {
                    // if the value is binary
                    inv = fromNumber(Integer.valueOf(value));
                }
                return inv;
            }
            for (InvokeParams inv : InvokeParams.values())
            {
                if (inv.toString().endsWith(value))
                {
                    return inv;
                }
            }
            return ERROR;
        }

        public int getState()
        {
            return state;
        }
    }

    InvokeParams invokeParams;

    public Handler(Object object, Method method, String basePath, RestfulHandlerConfig handlerConfig)
    {
        this.basePath = basePath;
        this.object = object;
        this.method = method;
        this.handlerConfig = handlerConfig;
        Class<?>[] types = method.getParameterTypes();
        this.requestType = types[types.length - 1];

        // request context session response.
        StringBuffer sb = new StringBuffer();

        // request
        if (ContentType.JSON.equals(handlerConfig.getRequestContentType()))
        {
            sb.append("1");
        }
        else
        {
            sb.append("0");
        }

        if (SessionType.SESSION.equals(handlerConfig.getSessionType()))
        {
            sb.append("1");
        }
        else
        {
            sb.append("0");
        }

        // context , default is need context
        sb.append("1");

        if (ContentType.JSON.equals(handlerConfig.getResponseContentType()))
        {

            sb.append("1");
        }
        else
        {
            sb.append("0");
        }

        invokeParams = InvokeParams.fromString(sb.toString());
    }

    public Handler(Object object, Method method, String basePath, Rest annotator)
    {
        this(object, method, basePath, RestfulHandlerConfig.createHandlerConfig(annotator));
    }

    public Class<?> getRequestType()
    {
        return requestType;
    }

    void invoke(HttpContext context) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
    {
        method.invoke(this.object, context);
    }

    Object invokeAndReturn(HttpContext context) throws IllegalArgumentException, IllegalAccessException,
                    InvocationTargetException
    {
        return method.invoke(this.object, context);
    }

    void invoke(HttpContext context, Object object) throws IllegalArgumentException, IllegalAccessException,
                    InvocationTargetException
    {
        method.invoke(this.object, context, object);
    }

    Object invokeAndReturn(HttpContext context, Object object) throws IllegalArgumentException, IllegalAccessException,
                    InvocationTargetException
    {
        return method.invoke(this.object, context, object);
    }

    void invokeSession(HttpContext context, Object session) throws IllegalArgumentException, IllegalAccessException,
                    InvocationTargetException
    {
        method.invoke(this.object, context, session);
    }

    Object invokeSessionAndReturn(HttpContext context, Object session) throws IllegalArgumentException,
                    IllegalAccessException, InvocationTargetException
    {
        return method.invoke(this.object, context, session);
    }

    void invokeSession(HttpContext context, Object session, Object object) throws IllegalArgumentException,
                    IllegalAccessException, InvocationTargetException
    {
        method.invoke(this.object, context, session, object);
    }

    Object invokeSessionAndReturn(HttpContext context, Object session, Object object) throws IllegalArgumentException,
                    IllegalAccessException, InvocationTargetException
    {
        return method.invoke(this.object, context, session, object);
    }

    /**
     * 
     * @param args should be follow below input parameter order. <li>1. HttpContext Object</li> <li>
     *            2. Session</li> <li>3. Request Object</li>
     * @return
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    Object invoke(HttpContext context, Object... args) throws IllegalAccessException, IllegalArgumentException,
                    InvocationTargetException
    {
        if (HttpMethod.OPTIONS.toString().equals(handlerConfig.getMethod()))
        {
            Object[] objs = new Object[] { this };
            if (null == args)
            {
                args = new Object[] {};
            }
            args = ArrayUtils.addAll(args, objs);
        }
        if (null == args || args.length == 0)
        {
            return method.invoke(this.object, context);
        }

        Object[] objs = new Object[] { context };
        args = ArrayUtils.addAll(objs, args);

        return method.invoke(this.object, args);
    }

    public RestfulHandlerConfig getHandlerConfig()
    {
        return handlerConfig;
    }

    public void setHandlerConfig(RestfulHandlerConfig handlerConfig)
    {
        this.handlerConfig = handlerConfig;
    }

    public boolean isInternal()
    {
        return handlerConfig.isInternal();
    }

    public boolean isDebug()
    {
        return handlerConfig.isDebug();
    }

    public RSFFilter getFilter()
    {
        return filters.isEmpty() ? null : filters.getLast();
    }

    public void setFilter(RSFFilter filter)
    {
        if (filters.isEmpty())
        {
            this.filter = filter;
        }
        addFilter(filter);
    }

    public void addFilter(RSFFilter filter)
    {
        if (null != filter)
        {
            filters.add(filter);
        }
    }

    public List<RSFFilter> getFilterList()
    {
        return new ArrayList<RSFFilter>(filters);
    }

    public List<RSFFilter> getReverseFilterList()
    {
        List<RSFFilter> list = getFilterList();
        java.util.Collections.reverse(list);
        return list;
    }
}
